msg_tool\scripts\favorite/
hcb.rs1use super::disasm::*;
3use crate::ext::io::*;
4use crate::scripts::base::*;
5use crate::types::*;
6use crate::utils::encoding::*;
7use anyhow::Result;
8
9#[derive(Debug)]
10pub struct HcbScriptBuilder {}
12
13impl HcbScriptBuilder {
14 pub fn new() -> Self {
16 Self {}
17 }
18}
19
20impl ScriptBuilder for HcbScriptBuilder {
21 fn default_encoding(&self) -> Encoding {
22 Encoding::Cp932
23 }
24
25 fn build_script(
26 &self,
27 buf: Vec<u8>,
28 _filename: &str,
29 encoding: Encoding,
30 _archive_encoding: Encoding,
31 config: &ExtraConfig,
32 _archive: Option<&Box<dyn Script>>,
33 ) -> Result<Box<dyn Script>> {
34 Ok(Box::new(HcbScript::new(buf, encoding, config)?))
35 }
36
37 fn extensions(&self) -> &'static [&'static str] {
38 &["hcb"]
39 }
40
41 fn script_type(&self) -> &'static ScriptType {
42 &ScriptType::Favorite
43 }
44}
45
46#[derive(Debug)]
47pub struct HcbScript {
48 data: Data,
49 reader: MemReader,
50 custom_yaml: bool,
51 filter_ascii: bool,
52 encoding: Encoding,
53}
54
55impl HcbScript {
56 pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
57 let reader = MemReader::new(buf);
58 let data = Data::disasm(reader.to_ref(), encoding)?;
59 Ok(Self {
60 data,
61 reader,
62 custom_yaml: config.custom_yaml,
63 filter_ascii: config.favorite_hcb_filter_ascii,
64 encoding,
65 })
66 }
67}
68
69impl Script for HcbScript {
70 fn default_output_script_type(&self) -> OutputScriptType {
71 OutputScriptType::Json
72 }
73
74 fn default_format_type(&self) -> FormatOptions {
75 FormatOptions::None
76 }
77
78 fn is_output_supported(&self, _: OutputScriptType) -> bool {
79 true
80 }
81
82 fn custom_output_extension<'a>(&'a self) -> &'a str {
83 if self.custom_yaml { "yaml" } else { "json" }
84 }
85
86 fn extract_messages(&self) -> Result<Vec<Message>> {
87 let mut messages = Vec::new();
88 for funcs in [&self.data.functions, &self.data.main_script] {
89 for func in funcs {
90 for operand in &func.operands {
91 if let Operand::S(s) = operand {
92 if self.filter_ascii && s.chars().all(|c| c.is_ascii()) {
93 continue;
94 }
95 messages.push(Message::new(s.clone(), None));
96 }
97 }
98 }
99 }
100 Ok(messages)
101 }
102
103 fn import_messages<'a>(
104 &'a self,
105 messages: Vec<Message>,
106 file: Box<dyn WriteSeek + 'a>,
107 _filename: &str,
108 encoding: Encoding,
109 replacement: Option<&'a ReplacementTable>,
110 ) -> Result<()> {
111 let mut mess = messages.iter();
112 let mut mes = mess.next();
113 let mut patcher =
114 BinaryPatcher::new(self.reader.to_ref(), file, |pos| Ok(pos), |pos| Ok(pos))?;
115 let mut need_pacth_addresses = Vec::new();
116 for funcs in [&self.data.functions, &self.data.main_script] {
117 for func in funcs {
118 let mut cur_pos = func.pos + 1;
119 if matches!(func.opcode, 0x02 | 0x06 | 0x07) {
120 need_pacth_addresses.push(cur_pos);
121 }
122 for operand in &func.operands {
123 if let Operand::S(s) = operand {
124 if self.filter_ascii && s.chars().all(|c| c.is_ascii()) {
125 continue;
126 }
127 let m = match mes {
128 Some(m) => m,
129 None => {
130 return Err(anyhow::anyhow!(
131 "Not enough messages to import. Missing message: {}",
132 s
133 ));
134 }
135 };
136 let mut message = m.message.clone();
137 if let Some(table) = replacement {
138 for (k, v) in &table.map {
139 message = message.replace(k, v);
140 }
141 }
142 patcher.copy_up_to(cur_pos)?;
143 let ori_len = operand.len(self.encoding)? as u64;
144 let mut s = encode_string(encoding, &message, true)?;
145 s.push(0); let len = s.len();
147 if len > 255 {
148 return Err(anyhow::anyhow!(
149 "Message too long to import (max 255 bytes): {}",
150 message
151 ));
152 }
153 patcher.replace_bytes_with_write(ori_len, |writer| {
154 writer.write_u8(len as u8)?;
155 writer.write_all(&s)?;
156 Ok(())
157 })?;
158 mes = mess.next();
159 }
160 cur_pos += operand.len(self.encoding)? as u64;
161 }
162 }
163 }
164 patcher.copy_up_to(self.reader.data.len() as u64)?;
165 for addr in need_pacth_addresses {
166 patcher.patch_u32_address(addr)?;
167 }
168 Ok(())
169 }
170
171 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
172 let s = if self.custom_yaml {
173 serde_yaml_ng::to_string(&self.data)?
174 } else {
175 serde_json::to_string_pretty(&self.data)?
176 };
177 let e = encode_string(encoding, &s, false)?;
178 let mut writer = crate::utils::files::write_file(filename)?;
179 writer.write_all(&e)?;
180 Ok(())
181 }
182}